/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.game.math.view.expression

import java.text.DecimalFormat;
import java.text.NumberFormat;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
import javax.script.ScriptEngineManager;
import java.lang.StringBuilder;
import java.lang.StringBuilder;
import java.util.logging.Level;
import java.util.logging.Logger;
import scala.collection.Set;
import org.game.math.view.domain.MathExpression;
import nl.flotsam.xeger.Xeger;


class GenerateExpression {
  protected def generateNumberGrammar(signed:Boolean, length:Int):String = {
    val numberGrammar:StringBuilder = new StringBuilder();

    if (signed) {
      numberGrammar.append("[\\-]{0,1}");
    }
    numberGrammar.append("[0-9]{1,").append(length).append("}");
    numberGrammar.toString();

  }

  protected def generateOperatorGrammar(operators:Set[Enumeration#Value]):String  = {
    val operatorGrammar:StringBuilder = new StringBuilder("[");
    operators.foreach(operator => {
        operator match {
          case Operator.SUB => operatorGrammar.append("\\").append(Operator.getOperatorChar(Operator.SUB));
          case x:Operator.Operator => operatorGrammar.append(Operator.getOperatorChar(x));
        }
      });


    operatorGrammar.append("]");
    operatorGrammar.toString();
  }
  protected def generateExpressionGrammar(operators:Set[Enumeration#Value], numOperand:Int, signed:Boolean, length:Int):String  = {
    val expressionGrammar:StringBuilder  = new StringBuilder();
    val numberGrammar:String = generateNumberGrammar(signed, length);
    val operatorGrammar:String  = generateOperatorGrammar(operators);
    expressionGrammar.append(numberGrammar);
    for (i<- 0 until numOperand) {
      expressionGrammar.append(operatorGrammar).append(numberGrammar);
    }
    expressionGrammar.toString();
  }

  def generateExpression(expNum:Int, operators:Set[Enumeration#Value], numOperand:Int, signed:Boolean, length:Int):List[MathExpression] =  {
    var expressions:List[MathExpression] = List[MathExpression]();

    val expressionGrammar = generateExpressionGrammar(operators, numOperand, signed, length);
    val expressionGen:Xeger = new Xeger(expressionGrammar);
    var i:Int = 0;
    val se:ScriptEngine = GenerateExpression.scriptEngine;
    while (i < expNum) {
      var mathExpression = new MathExpression();
      mathExpression.expression = expressionGen.generate();
      try {
        val value:Double = se.eval(mathExpression.expression).asInstanceOf[Double];
        if (Double.NegativeInfinity != value || Double.PositiveInfinity != value) {
          mathExpression.result=GenerateExpression.numberFormat.format(value);
          mathExpression.corrected = false;
          i +=1;
        }
      } catch {case ex:ScriptException =>
          Logger.getLogger(classOf[GenerateExpression].getName).log(Level.SEVERE, null, ex);
      };
      expressions = expressions :+ mathExpression;
    }
    expressions;
  }
  def generateMultiplyTable(multipliedNum:Int):List[MathExpression] = {
    var expressions:List[MathExpression] = List();
    var result:Int = 0;
    for (i<- 1 to 10) {
      var mathExpression:MathExpression = new MathExpression();
      mathExpression.expression = "" + multipliedNum + "x" + i;
      mathExpression.inputResult = "";
      mathExpression.corrected = false;
      result += multipliedNum;
      mathExpression.result = result.toString();
      expressions = expressions :+ mathExpression;
    }
    expressions;
  }
}

object GenerateExpression {

  val scriptEngineManager:ScriptEngineManager = new ScriptEngineManager();
  val scriptEngine:ScriptEngine = scriptEngineManager.getEngineByName("JavaScript");
  val numberFormat:NumberFormat = new DecimalFormat("####.##");
  val generateExpression:GenerateExpression = new GenerateExpression();
  def getInstance():GenerateExpression = {
    GenerateExpression.generateExpression;
  }
}
